package com.skywolf.chem.match.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.skywolf.chem.common.config.data.DataSource;
import com.skywolf.chem.common.config.data.DataSourceNames;
import com.skywolf.chem.common.enums.ResponseCodeEnum;
import com.skywolf.chem.common.sysconfig.exception.BizException;
import com.skywolf.chem.match.common.constants.MatchConstants;
import com.skywolf.chem.match.common.enums.MatchGroupStatusEnum;
import com.skywolf.chem.match.entity.ApplyUserRecord;
import com.skywolf.chem.match.entity.GroupInfo;
import com.skywolf.chem.match.mapper.ApplyUserInfoMapper;
import com.skywolf.chem.match.mapper.ApplyUserRecordMapper;
import com.skywolf.chem.match.mapper.GroupInfoMapper;
import com.skywolf.chem.match.mapper.OrderMapper;
import com.skywolf.chem.match.params.param.ApplyDeletedUserParam;
import com.skywolf.chem.match.params.param.ApplyUserInfoParam;
import com.skywolf.chem.match.params.param.ApplyUserListParam;
import com.skywolf.chem.match.service.IApplyUserInfoService;
import com.skywolf.chem.match.service.IApplyUserRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.skywolf.chem.match.utils.LocalDateUtils;
import com.skywolf.chem.match.utils.RedisUtils;
import com.skywolf.chem.match.utils.StringUtils;
import lombok.AllArgsConstructor;
import org.checkerframework.checker.units.qual.A;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author
 * @since 2021-09-26
 */
@Service
@AllArgsConstructor
public class ApplyUserRecordServiceImpl extends ServiceImpl<ApplyUserRecordMapper, ApplyUserRecord> implements IApplyUserRecordService {

    private final ApplyUserRecordMapper userRecordMapper;

    private final OrderMapper orderMapper;

    private final GroupInfoMapper groupInfoMapper;

    private final RedisUtils redisUtils;

    private final IApplyUserInfoService applyUserInfoService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @DataSource(DataSourceNames.TWO)
    public void insertAndVerifyApplyUser(Long userId, ApplyUserInfoParam param) throws Exception {
        /*
         * 验证流程:
         *      初试参数验证(非空等)->>    组别兼报验证->>    重复报名验证->>    组别参赛条件验证->>
         *      参数验证错误驳回
         *      参数验证通过通过用户编号，组别编号，活动编号生成redis加密key
         *      采用redis hash存储
         *      key:用户编号，组别编号，活动编号生成redis加密key
         *      item:自定义唯一用户uuid
         *      value:报名人详细信息
         *
         */
        verifyApplyUser(userId, param);
        insertApplyUser(userId, param);
        applyUserInfoService.saveApplyUser(userId, param);
    }

    @Override
    @DataSource(DataSourceNames.TWO)
    public void updateApplyUser(Long userId, ApplyUserInfoParam param) throws Exception {
        if (StringUtils.isBlank(param.getKey())) {
            throw new BizException("更新时用户报名key不能为空！");
        }
        String keys = MatchConstants.APPLY_USER_INFO_KEY + userId + "-" + param.getMatchId() + "-" + param.getMatchGroupId();
        verifyApplyUser(userId, param);
        //先删后增
        deletedApplyUser(keys, param.getKey());
        insertApplyUser(userId, param);
    }

    @Override
    @DataSource(DataSourceNames.TWO)
    public void deletedApplyUser(Long userId, ApplyDeletedUserParam param) throws Exception {
        String keys = MatchConstants.APPLY_USER_INFO_KEY + userId + "-" + param.getMatchId() + "-" + param.getMatchGroupId();
        deletedApplyUser(keys, param.getKey());
    }

    @Override
    @DataSource(DataSourceNames.TWO)
    public List<ApplyUserInfoParam> getApplyUserList(Long userId, ApplyUserListParam param) {
        String keys = MatchConstants.APPLY_USER_INFO_KEY + userId + "-" + param.getMatchId() + "-" + param.getMatchGroupId();
        if (redisUtils.hasKey(keys)) {
            Map<String, Object> userInfoMap = redisUtils.hmget(keys);
            return (null != userInfoMap && userInfoMap.size() > 0) ? userInfoMap.values().parallelStream().
                    map(item -> (ApplyUserInfoParam) item).collect(Collectors.toList()) : new ArrayList<ApplyUserInfoParam>();
        } else {
            return new ArrayList<>();
        }
    }

    @Override
    @DataSource(DataSourceNames.TWO)
    public void clearApplyUserList(Long userId, ApplyUserListParam param) {
        String keys = MatchConstants.APPLY_USER_INFO_KEY + userId + "-" + param.getMatchId() + "-" + param.getMatchGroupId();
        redisUtils.del(keys);
    }

    /**
     * 报名人信息验证
     */
    @DataSource(DataSourceNames.TWO)
    private void verifyApplyUser(Long userId, ApplyUserInfoParam param) throws Exception {
        //查询当前用户报名的组别信息
        GroupInfo groupInfo = groupInfoMapper.selectById(param.getMatchGroupId());
        if (null == groupInfo) {
            throw new BizException(ResponseCodeEnum.GROUP_NOT_EXIST);
        }
        if (!groupInfo.getGroupState().equals(MatchGroupStatusEnum.APPLYING.getStatus())) {
            throw new BizException("当前组别状态不可报名");
        }
        //验证参赛剩余人数
        if ((groupInfo.getStock() - groupInfo.getLockedStock()) <= 0) {
            throw new BizException("此组别报名人数暂时已满！请稍后再试！");
        }
        // 兼报判断
        if (StringUtils.isNotBlank(groupInfo.getEnrolls())) {
            //查询当前用户报名的同一个活动其他组别名称列表
            List<String> applyGroupIdList = orderMapper.getApplyMatchGroupIds(userId, param.getMatchId());
            //当前报名的组别可简报的其他组别名称
            List<String> legalGroup = new ArrayList<>(Arrays.asList(groupInfo.getEnrolls().split(",")));
            if (!legalGroup.containsAll(applyGroupIdList)) {
                applyGroupIdList.removeAll(legalGroup);
                StringBuilder errMsg = new StringBuilder("您已报名：");
                applyGroupIdList.forEach(item -> errMsg.append(" ").append(item));
                errMsg.append(" 与当前组别不能和这些同时报名！");
                throw new BizException(errMsg.toString());
            }
        }

        // 重复报名判断
        ApplyUserRecord applyUserRecord = userRecordMapper.selectOne(new LambdaQueryWrapper<ApplyUserRecord>()
                .eq(ApplyUserRecord::getMatchGroupId, param.getMatchGroupId())
                .eq(ApplyUserRecord::getMatchId, param.getMatchId())
                .eq(ApplyUserRecord::getName, param.getName())
                //状态为正常报名状态
                .notIn(ApplyUserRecord::getApplyStatus, 102, 106));
        //在同一个活动同一个组别中有相同名称的报名记录    ->>重复报名
        if (null != applyUserRecord) {
            throw new BizException("已存在相同报名信息！请勿重复报名！");
        }
        String keys = MatchConstants.APPLY_USER_INFO_KEY + userId + "-" + param.getMatchId() + "-" + param.getMatchGroupId();
        //已经添加过一个相同名称的报名记录    ->>重复报名
        if (redisUtils.hasKey(keys)) {
            Map<String, Object> userInfoMap = redisUtils.hmget(keys);
            long count = userInfoMap.values().stream().map(item -> (ApplyUserInfoParam) item).filter(item -> item.getName().equals(param.getName())).count();
            if (count > 0) {
                throw new BizException("已存在相同报名信息！请勿重复报名！");
            }
        }
        //获取我的生日
        LocalDateTime myBirthday = LocalDateUtils.parseLocalDateTime(StringUtils.checkBrithday(param.getIdCard()), LocalDateUtils.DATETIME_PATTERN);
        //验证当前组别报名条件
        System.out.println(LocalDateUtils.compareDate(myBirthday, groupInfo.getMinAge()));
        System.out.println(LocalDateUtils.compareDate(myBirthday, groupInfo.getMaxAge()));
        //验证年龄
        if ((LocalDateUtils.compareDate(myBirthday, groupInfo.getMinAge()) == -1 || LocalDateUtils.compareDate(myBirthday, groupInfo.getMaxAge()) == 1)) {
            throw new BizException("报名的年龄不在参赛年龄段之间");
        }

        //验证性别不为不限时
        if (groupInfo.getGender() != 2) {
            if (!groupInfo.getGender().equals(param.getGender())) {
                String genderStr = groupInfo.getGender() == 1 ? "男" : "女";
                throw new BizException("此活动只允许性别为：" + genderStr + "的选手报名！");
            }
        }
        if (!StringUtils.isEmpty(param.getTagModels())) {
            //验证自定义参数
            List<ApplyUserInfoParam.ApplyUserCustomTagModel> tagModels = param.getTagModels();
            for (ApplyUserInfoParam.ApplyUserCustomTagModel item : tagModels) {
                //当前自定义标签为必填项
                if ("true".equals(item.getRequired().toLowerCase())) {
                    if (null == item.getValue() || item.getValue().size() == 0) {
                        throw new BizException("表单" + item.getKey() + "为必填项! 不能为空！");
                    }
                }
            }
        }
    }

    /**
     * 添加报名人
     */
    private void insertApplyUser(Long userId, ApplyUserInfoParam param) throws Exception {
        String keys = MatchConstants.APPLY_USER_INFO_KEY + userId + "-" + param.getMatchId() + "-" + param.getMatchGroupId();
        //当前用户以前添加 同活动同组别的报名用户信息    ——>>执行更新
        if (redisUtils.hasKey(keys)) {
            Map<String, Object> userInfoMap = redisUtils.hmget(keys);
            String itemKey = StringUtils.getRandomString(8);
            if (StringUtils.isNotBlank(param.getKey())) {
                itemKey = param.getKey();
            }
            param.setKey(itemKey);
            userInfoMap.put(itemKey, param);
            redisUtils.hmset(keys, userInfoMap);
        } else {
            //第一次添加同活动同组别的报名用户信息      ->>执行新增
            Map<String, Object> userInfoMap = new HashMap<>(1);
            String itemKey = StringUtils.getRandomString(8);
            if (StringUtils.isNotBlank(param.getKey())) {
                itemKey = param.getKey();
            }
            param.setKey(itemKey);
            userInfoMap.put(itemKey, param);
            redisUtils.hmset(keys, userInfoMap);
        }
    }

    /**
     * 根据名称删除指定报名人
     */
    private void deletedApplyUser(String key, String item) {
        redisUtils.hdel(key, item);
    }
}
